using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Permissions;
using System.Collections.Specialized;

namespace Zsa.Emulation.Util
{
    /// <summary>
    /// Trace source.
    /// </summary>

    public class DebugTraceSource
    {
        #region Static 
        /// <summary>
        /// The max writes.
        /// </summary>
        private static int _DefaultMaxWrites;

        /// <summary>
        /// Gets or sets the default max writes.
        /// </summary>
        /// <value>The default max writes.</value>
        public static int DefaultMaxWrites
        {
            get
            {
                return _DefaultMaxWrites;
            }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentOutOfRangeException("value", value, "Max writes must be greater than zero.  Or use 0 to flush on every write.");
                }
                _DefaultMaxWrites = value;
            }
        }
        #endregion Static

        #region Fields
        /// <summary>
        /// The max writes.
        /// </summary>
        private int _MaxWrites;

        /// <summary>
        /// The total writes.
        /// </summary>
        private int _TotalWrites;

        /// <summary>
        /// The last line.
        /// </summary>
        private string _LastLine;

        /// <summary>
        /// The repeat count.
        /// </summary>
        private int _RepeatCount;

        /// <summary>
        /// The subsystem.
        /// </summary>
        private TraceSubsystem _SubSystem;

        /// <summary>
        /// The source.
        /// </summary>
        private TraceSource _Source;
        #endregion Fields

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DebugTraceSource"/> class.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        public DebugTraceSource(TraceSubsystem subsys)
            : this(subsys, SourceLevels.All, DefaultMaxWrites)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DebugTraceSource"/> class.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="count">The count.</param>
        public DebugTraceSource(TraceSubsystem subsys, int count)
            : this(subsys, SourceLevels.All, count)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DebugTraceSource"/> class.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="eventType">Type of the event.</param>
        public DebugTraceSource(TraceSubsystem subsys, SourceLevels levels)
            : this(subsys, levels, DefaultMaxWrites)
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:DebugTraceSource"/> class.
        /// </summary>
        /// <param name="subsys">The subsys.</param>
        /// <param name="levels">The levels.</param>
        /// <param name="maxWrites">The max writes.</param>
        public DebugTraceSource(TraceSubsystem subsys,  SourceLevels levels,
            int maxWrites)
            
        {
            _Source = new TraceSource(subsys.ToString(), levels);
            _MaxWrites = maxWrites;
            _SubSystem = subsys;
            
        }


        #endregion Constructors

        #region Properties
        /// <summary>
        /// Gets the name of the trace source.
        /// </summary>
        /// <value>The name of the trace source..</value>
        public string Name
        {
            get
            {
                return _Source.Name;
            }

        }

        /// <summary>
        /// Gets the custom switch attributes defined in the application configuration file.
        /// </summary>
        /// <value>A System.Collections.Specialized.StringDictionary containing the custom attributes
        /// for the trace switch..</value>
        public StringDictionary Attributes
        {
            get
            {
                return _Source.Attributes;
            }
        }

        /// <summary>
        /// Gets the collection of trace listeners for the trace source.
        /// </summary>
        /// <value>A System.Diagnostics.TraceListenerCollection that contains the active trace 
        /// listeners associated with the source..</value>
        public TraceListenerCollection Listeners
        {
            get
            {
                return _Source.Listeners;
            }
        }
   
        /// <summary>
        /// Gets or sets the source switch value.
        /// </summary>
        /// <value>A System.Diagnostics.SourceSwitch object representing the source switch value.</value>
        /// <exception cref="System.ArgumentNullException">System.Diagnostics.TraceSource.Switch is set to null.</exception>
        public SourceSwitch Switch
        {
            get
            {
                return _Source.Switch;
            }
            set
            {
                _Source.Switch = value;
            }
        }

        /// <summary>
        /// Gets the sub system.
        /// </summary>
        /// <value>The sub system.</value>
        public TraceSubsystem SubSystem
        {
            get
            {
                return _SubSystem;
            }
        }

        public int MaxWrites
        {
            get
            {
                return _MaxWrites;
            }
            set
            {
                _MaxWrites = value;
            }
        }
        #endregion Properties

        #region Methods
        /// <summary>
        /// Closes all the trace listeners in the trace listener collection.
        /// </summary>
        [Conditional("TRACE")]
        public void Close()
        {
            _Source.Close();
        }

        /// <summary>
        /// Flushes all the trace listeners in the trace listener collection.
        /// </summary>
        [Conditional("TRACE")]
        public void Flush()
        {
            if (_RepeatCount > 0)
            {
                _Source.TraceInformation("Last message repeated {0} times before flush.",_RepeatCount);
                _RepeatCount = 0;
            }
            _Source.Flush();
            _TotalWrites = 0;
        }
        
        /// <summary>
        /// Writes trace data to the trace listeners in the System.Diagnostics.TraceSource.Listeners 
        /// collection using the specified event type, event identifier, and trace data.
        /// </summary>
        /// <param name="eventType">
        /// One of the System.Diagnostics.TraceEventType values that specifies the event type of the trace data.
        /// </param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="data">The trace data.</param>
        [Conditional("TRACE")]
        public void TraceData(TraceEventType eventType, int id, object data)
        {
            _Source.TraceData(eventType, id, data);
        }

        /// <summary>
        /// Writes trace data to the trace listeners in the System.Diagnostics.TraceSource.Listeners collection using the specified event type, event identifier, and trace data array.
        /// </summary>
        /// <param name="eventType">One of the System.Diagnostics.TraceEventType values that specifies the event type of the trace data.</param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="data">An object array containing the trace data..</param>
        [Conditional("TRACE")]
        public void TraceData(TraceEventType eventType, int id, params object[] data)
        {
            _Source.TraceData(eventType, id, data);
        }

        /// <summary>
        /// Writes a trace event message to the trace listeners in the System.Diagnostics.TraceSource.Listeners collection using the specified event type and event identifier.
        /// </summary>
        /// <param name="eventType">One of the System.Diagnostics.TraceEventType values that specifies the event type of the trace data.</param>
        /// <param name="id">A numeric identifier for the event.</param>
        [Conditional("TRACE")]
        public void TraceEvent(TraceEventType eventType, int id)
        {
            _Source.TraceEvent(eventType, id);
        }


        /// <summary>
        /// Writes a trace event message to the trace listeners in the System.Diagnostics.TraceSource.Listeners collection using the specified event type, event identifier, and message.
        /// </summary>
        /// <param name="eventType">One of the System.Diagnostics.TraceEventType values that specifies the event type of the trace data.</param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="message">The trace message to write.</param>
        [Conditional("TRACE")]
        public void TraceEvent(TraceEventType eventType, int id, string message)
        {
            _Source.TraceEvent(eventType, id, message);
        }

        /// <summary>
        /// Writes a trace event to the trace listeners in the System.Diagnostics.TraceSource.Listeners collection using the specified event type, event identifier, and argument array and format.
        /// </summary>
        /// <param name="eventType">One of the System.Diagnostics.TraceEventType values that specifies the event type of the trace data.</param>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="format">A format string that contains zero or more format items, which correspond to objects in the args array.</param>
        /// <param name="args">An object array containing zero or more objects to format.</param>
        [Conditional("TRACE")]
        public void TraceEvent(TraceEventType eventType, int id, string format, params object[] args)
        {
            _Source.TraceEvent(eventType, id, format, args);
        }

        /// <summary>
        /// Writes an informational message to the trace listeners in the System.Diagnostics.TraceSource.Listeners collection using the specified message.
        /// </summary>
        /// <param name="message">The informative message to write.</param>
        [Conditional("TRACE")]
        public void TraceInformation(string message)
        {
            TraceInformation(message,null);
        }

        /// <summary>
        /// Writes an informational message to the trace listeners in the System.Diagnostics.TraceSource.Listeners
        /// collection using the specified object array and formatting information.
        /// </summary>
        /// <param name="format">A format string that contains zero or more format items that correspond to objects in the args array.</param>
        /// <param name="args">An array containing zero or more objects to format.</param>
        [Conditional("TRACE")]
        public void TraceInformation(string format, params object[] args)
        {
            string _Message = args != null ? string.Format(format, args) : format;
            if (_Message == _LastLine)
            {
                _RepeatCount++;
            }
            else
            {
                if (_RepeatCount > 0)
                {
                    _Source.TraceInformation("Last message repeated {0} times",_RepeatCount);
                }
                _RepeatCount = 0;
                _LastLine = _Message;
                _Source.TraceInformation(_Message);
            }
            _TotalWrites++;
            if (_TotalWrites > _MaxWrites)
            {
                Flush();
            }
            
            
        }

        /// <summary>
        /// Writes trace transfer message to the trace listeners in the System.Diagnostics.TraceSource.Listeners
        /// collection using the specified numeric identifier, message, and related activity identifier.
        /// </summary>
        /// <param name="id">A numeric identifier for the event.</param>
        /// <param name="message">The trace message to write.</param>
        /// <param name="relatedActivityId">A System.Guid structure that identifies the related activity.</param>
        [Conditional("TRACE")]
        public void TraceTransfer(int id, string message, Guid relatedActivityId)
        {
            _Source.TraceTransfer(id, message, relatedActivityId);
        }
        #endregion Methods
    }
}
